﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CompoundUseCase.cs" company="">
//   
// </copyright>
// <summary>
//   The compound use case.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Libium.UseCases
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    ///   The compound use case.
    /// </summary>
    public abstract class CompoundUseCase : UseCase, ICompoundUseCase
    {
        #region Fields

        /// <summary>
        ///   The _use cases.
        /// </summary>
        private readonly List<IUseCase> _useCases = new List<IUseCase>();

        /// <summary>
        ///   The _active use case.
        /// </summary>
        private ICompoundUseCase _activeUseCase;

        private bool _privateEmpty;

        private bool _privateModified;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="CompoundUseCase"/> class.
        /// </summary>
        /// <param name="parent">
        /// The parent. 
        /// </param>
        /// <param name="service">
        /// The service. 
        /// </param>
        protected CompoundUseCase(ICompoundUseCase parent, IUseCaseInstanceService service)
            : base(parent, service)
        {
        }

        #endregion

        #region Public Events

        /// <summary>
        ///   The active use case changed.
        /// </summary>
        public event Action<ICompoundUseCase> ActiveUseCaseChanged;

        /// <summary>
        ///   The use case added.
        /// </summary>
        public event Action<ICompoundUseCase, IUseCase> UseCaseAdded;

        /// <summary>
        ///   The use case deleted.
        /// </summary>
        public event Action<ICompoundUseCase, IUseCase> UseCaseDeleted;

        #endregion

        #region Public Properties

        /// <summary>
        ///   Gets the active use case.
        /// </summary>
        public IUseCase ActiveUseCase
        {
            get
            {
                return _activeUseCase;
            }
        }

        public override bool Empty
        {
            protected set
            {
                if (_privateEmpty == value)
                {
                    return;
                }

                _privateEmpty = value;
                UpdateBaseEmpty();
            }
        }

        public override bool Modified
        {
            protected set
            {
                if (_privateModified == value)
                {
                    return;
                }

                _privateModified = value;
                UpdateBaseModified();
            }
        }

        /// <summary>
        ///   Gets the use cases.
        /// </summary>
        public ICollection<IUseCase> UseCases
        {
            get
            {
                return _useCases;
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The add check use case.
        /// </summary>
        /// <param name="getFunc">
        /// The get func. 
        /// </param>
        /// <param name="setAction">
        /// The set action. 
        /// </param>
        /// <returns>
        /// The <see cref="ICheckUseCase"/> . 
        /// </returns>
        public virtual ICheckUseCase AddCheckUseCase(Func<bool> getFunc, Action<bool> setAction)
        {
            var useCase = new FuncCheckUseCase(this, Service, getFunc, setAction);
            AddUseCase(useCase);
            return useCase;
        }

        /// <summary>
        /// The add choice use case.
        /// </summary>
        /// <param name="getFunc">
        /// The get func. 
        /// </param>
        /// <param name="setAction">
        /// The set action. 
        /// </param>
        /// <param name="getValuesFunc">
        /// The get values func. 
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// The <see cref="IChoiceUseCase"/> . 
        /// </returns>
        public virtual IChoiceUseCase AddChoiceUseCase<T>(
            Func<T> getFunc, Action<T> setAction, Func<IEnumerable<INamedValue<T>>> getValuesFunc)
        {
            var useCase = new FuncChoiceUseCase<T>(this, Service, getFunc, setAction, getValuesFunc);
            AddUseCase(useCase);
            return useCase;
        }

        /// <summary>
        /// The add file use case.
        /// </summary>
        /// <param name="getFilePath">
        /// The get file path. 
        /// </param>
        /// <param name="setFilePath">
        /// The set file path. 
        /// </param>
        /// <returns>
        /// The <see cref="IFileUseCase"/> . 
        /// </returns>
        public virtual IFileUseCase AddFileUseCase(Func<string> getFilePath, Action<string> setFilePath)
        {
            var useCase = new FuncFileUseCase(this, Service, getFilePath, setFilePath);
            AddUseCase(useCase);
            return useCase;
        }

        /// <summary>
        /// The add image use case.
        /// </summary>
        /// <param name="getFilePath">
        /// The get file path. 
        /// </param>
        /// <param name="setFilePath">
        /// The set file path. 
        /// </param>
        /// <returns>
        /// The <see cref="IImageUseCase"/> . 
        /// </returns>
        public virtual IImageUseCase AddImageUseCase(Func<string> getFilePath, Action<string> setFilePath)
        {
            var useCase = new FuncImageUseCase(this, Service, getFilePath, setFilePath);
            AddUseCase(useCase);
            return useCase;
        }

        /// <summary>
        /// The add text edit use case.
        /// </summary>
        /// <param name="getFunc">
        /// The get func. 
        /// </param>
        /// <param name="setAction">
        /// The set action. 
        /// </param>
        /// <returns>
        /// The <see cref="ITextEditUseCase"/> . 
        /// </returns>
        public virtual ITextEditUseCase AddTextEditUseCase(Func<string> getFunc, Action<string> setAction)
        {
            var useCase = new FuncTextEditUseCase(this, Service, getFunc, setAction);
            AddUseCase(useCase);
            return useCase;
        }

        /// <summary>
        /// The add text use case.
        /// </summary>
        /// <param name="getFunc">
        /// The get func. 
        /// </param>
        /// <returns>
        /// The <see cref="ITextUseCase"/> . 
        /// </returns>
        public virtual ITextUseCase AddTextUseCase(Func<string> getFunc)
        {
            var useCase = new FuncTextUseCase(this, Service, getFunc);
            AddUseCase(useCase);
            return useCase;
        }

        /// <summary>
        /// The add use case.
        /// </summary>
        /// <param name="useCase">
        /// The use case. 
        /// </param>
        public void AddUseCase(IUseCase useCase)
        {
            _useCases.Add(useCase);
            OnUseCaseAdded(useCase);

            useCase.EmptyChanged += OnChildEmptyChanged;
            useCase.ModifiedChanged += OnChildModifiedChanged;

            UpdateBaseEmpty();
            UpdateBaseModified();
        }

        /// <summary>
        /// The delete use case.
        /// </summary>
        /// <param name="useCase">
        /// The use case. 
        /// </param>
        public void DeleteUseCase(IUseCase useCase)
        {
            _useCases.Remove(useCase);
            OnUseCaseDeleted(useCase);

            useCase.EmptyChanged -= OnChildEmptyChanged;
            useCase.ModifiedChanged -= OnChildModifiedChanged;

            UpdateBaseEmpty();
            UpdateBaseModified();

            // TODO Try to remove
            useCase.RaiseOnUnavailable();
        }

        #endregion

        #region Methods

        /// <summary>
        /// The on child empty changed.
        /// </summary>
        /// <param name="useCase">
        /// The use case. 
        /// </param>
        protected virtual void OnChildEmptyChanged(IUseCase useCase)
        {
            // TODO Add logic
            UpdateBaseEmpty();
        }

        /// <summary>
        /// The on child modified changed.
        /// </summary>
        /// <param name="useCase">
        /// The use case. 
        /// </param>
        protected virtual void OnChildModifiedChanged(IUseCase useCase)
        {
            // TODO Add logic
            UpdateBaseModified();
        }

        protected virtual void OnUseCaseAdded(IUseCase useCase)
        {
            if (UseCaseAdded != null)
            {
                UseCaseAdded(this, useCase);
            }
        }

        protected virtual void OnUseCaseDeleted(IUseCase useCase)
        {
            if (UseCaseDeleted != null)
            {
                UseCaseDeleted(this, useCase);
            }
        }

        private void UpdateBaseEmpty()
        {
            var empty = _privateEmpty;
            foreach (var useCase in UseCases)
            {
                empty &= useCase.Empty;
            }

            base.Empty = empty;
        }

        private void UpdateBaseModified()
        {
            var modified = _privateModified;
            foreach (var useCase in UseCases)
            {
                modified |= useCase.Modified;
            }

            base.Modified = modified;
        }

        #endregion

        public override void ResetModified()
        {
            base.ResetModified();

            _privateModified = false;

            foreach (var useCase in UseCases)
            {
                useCase.ResetModified();
            }
        }
    }
}